if (true) {
    let foo = 'sd'
}

//解构数组
// console.log(foo);
const arr = [1, , 5, 6]
const [, , baz] = arr;
console.log(baz)

const [foo, bar = 20, ...rest] = arr;
console.log(rest);
console.log(bar);

//解构对象

const obj = { name: 'sdf', age: 18 }
const { name: objName = 'jack' } = obj;
//const name = 'tom'//SyntaxError: Identifier 'name' has already been declared
console.log(objName);
const { log } = console;
log('foo')


//模板字符串
// const str = `hello es2015`
const name = 'tom'
const msg = `hey,${name}----${Math.random()}`
console.log(msg)
const gender = true
const str = console.log `hello world` //带标签的模板字符串

function myTagFunc(string, name, gender) {
    const sex = gender ? 'man' : 'woman'
    return string[0] + name + string[1] + gender + string[2]
}
const result = myTagFunc `hey,${name} is a ${gender}.`
console.log(result)


//字符串扩展
const message = 'Error:sdfsfsdfsdfsf,javascript'
console.log(message.startsWith("Error"))
console.log(message.endsWith('javascript'))
console.log(message.includes('sdfs'))

//参数默认值
function foo(enable) {
    //enable = enable || true//当传入false时，也会使用默认值
    enable = enable === undefined ? true : enable //默认值只有在参数为undefined时都会使用
    console.log('foo invoked - enable:')
    console.log(enable)
}
foo(false)

//剩余参数

// function foo() {
//     console.log(arguments)
// }

function foo(first, ...args) {
    console.log(args)
}
foo(1, 2, 3, 4)

//展开数组
const arr = ['foo', 'bar', 'baz']
    // console.log.apply(console, arr)

console.log(...arr)

//箭头函数
const arr = [1, 2, 3, 4, 5, 6, 7]
console.log(arr.filter(i => i % 2))

//箭头函数与this

const person = {
    name: 'tom',
    sayHi: () => {
        console.log(`hi, my name is ${this.name}`)
    },
    sayHiAsync: function() {
        // const _this = this
        // setTimeout(() => {
        //     console.log(_this.name)
        // }, 1000);
        setTimeout(() => {
            console.log(this.name)
        }, 1000)
    }
}
person.sayHiAsync()

//字面量增强
const bar = '345'
const obj = {
    foo: '123',
    // bar:bar,
    bar,
    // method1:function(){console.log('methid1')}
    method1() {
        console.log('method1')
        console.log(this)

    },
    [Math.random]: 123, //随机属性名
    [bar]: 123

}
obj[Math.random()] = 123
console.log(obj)

//对象扩展方法----Object.assign
const source1 = {
    a: 123,
    b: 123
}
const target = {
    a: 456,
    c: 456
}
const source2 = {
    b: 789,
    d: 798
}
const result = Object.assign(target, source1, source2)
console.log(target)
console.log(result === target)


function func(obj) {
    const funcObj = Object.assign({}, a = obj) //在函数内部把对象赋值到一个全新的对象上，避免对外部对象的数据产生影响
    funcObj.name = 'func obj'
    console.log(funcObj)
}
const obj = { name: 'global obj' }
func(obj)
console.log(obj)

//Object.is
//两个特殊例子
console.log(Object.is(NaN, NaN)) //true
console.log(NaN === NaN)
console.log(Object.is(+0, -0))
console.log(+0 === -0)

//proxy

const person = {
    name: 'zce',
    age: '20'
}
const personProxy = new Proxy(person, {
    get(target, property) {
        return property in target ? target[property] : 'default';
        // console.log(target, property)
        // return 100;
    },
    set(target, property, value) {
        //可以设置较验
        if (property === 'age') {
            if (!Number.isInteger(value)) {
                throw new TypeError(`${value} is not a int`)
            }
        }
        target[property] = value;
        // console.log(target, property, value)
    },
    deleteProperty(target, property) {
        console.log('delete', property);
        delete target[property]
    }
})
console.log(personProxy.name)
console.log(personProxy.xxx)
personProxy.name = "abc";
personProxy.age = 1;
console.log(personProxy.name);
console.log(personProxy.age);
delete personProxy.age;
console.log(person)

//proxy监视数组对象
const list = []
const listProxy = new Proxy(list, {
    set(target, property, value) {
        console.log('set', property, value)
        target[property] = value;
        return true; //表示设置成功
    }
})
listProxy.push(100)

//Reflect

const obj = {
    foo: '123',
    bar: '456'
}
const proxy = new Proxy(obj, { //代理默认使用的就是Reflect的底层方法
    //这里不定义set\get方法的话，就默认使用reflect的set\get方法
    // get(target, property) {
    //     console.log('watch logic..')
    //     return Reflect.get(target, property);
    // },
})
console.log(proxy.foo)

console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))

//class类
class Person {
    constructor(name) {
        this.name = name;
    }
    say() {
        console.log(`hi,my name is ${this.name}`)
    }
    static create(name) { //静态方法
        return new Person(name)
    }
}
// new Person('赵四').say()//用new实例化

//静态方法
//静态方法的this指向当前的类
const tom = Person.create('tom') //静态方法不用new
tom.say();

//类的继承
class Student extends Person {
    constructor(name, number) {
        super(name)
        this.number = number
    }
    hello() {
        super.say() //子类中调用父类的方法
        console.log(`my school number is${this.number}`)
    }
}
const s = new Student('jack', '100')
s.hello()

//Set

// const s = new Set()
// s.add(1).add(2).add(3).add(4).add(2);
// console.log(s.has(100))
// s.forEach(i => {
//     console.log(i)
// });
// for (let i of s) {
//     console.log(i)
// }
// console.log(s.size, s.delete(3), s)
// s.clear()
// console.log(s);

const arr = [1, 2, 3, 4, 5, 6, 2, 5];
//把数组转换成Set,再把Set展开成数组，就自动去重了
// const result = Array.from(new Set(arr)) //会去重
const result = [...new Set(arr)] //展开运算符
console.log(result)


//Map

//对象
// const obj = {}
// obj[true] = 'value'
// obj[123] = 'value'
// obj[{ a: 1 }] = 'value'
// console.log(Object.keys(obj))
// console.log(obj['[object Object]'])

//Map
const m = new Map()
const tom = { name: 'tom' }
m.set(tom, 90)
console.log(m)
console.log(m.get(tom))

m.forEach((value, key) => {
    console.log(value, key)
})

//Symbol

// const s = Symbol()
// console.log(s)
// console.log(typeof s)

// console.log(
//     Symbol() === Symbol()
// )//false

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2) //true
console.log(
    Symbol.for(true) === Symbol.for('true') //方法内部会自动转换成字符串
); //true
// const obj = {}
// obj[Symbol()] = '123'
// obj[Symbol()] = '456'
// console.log(obj)


const name = Symbol()
const person = {
    [name]: 'zce', //私有成员
    say() {
        console.log(this[name])
    }
};
// person[Symbol()]
Person.say()


//for of
//拿到的是数组中的每个元素，而不是对应的下标
const arr = [100, 222, 333]
for (const item of arr) {
    console.log(item); //获取到的是元素，不是下标
    if (item > 100) {
        break; //可随时终止循环
    }
}
const s = new Set(['foo', 'bar'])
for (const item of s) {
    console.log(item)
}

const m = new Map()
m.set('foo', '123')
m.set('bar', '345')
for (const [key, value] of m) {
    console.log(key, value)
}

// const obj = { foo: 123, bar: 465 }
// for (const item of obj) {
//     console.log(item)
// }//TypeError: obj is not iterable

//可迭代接口

const s = new Set(['foo', 'bar', 'baz'])
const iterator = s[Symbol.iterator]()

console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())

//实现iterator接口
// const obj = {
//     [Symbol.iterator]: function() {
//         return {
//             next: function() {
//                 return {
//                     value: 'zcs',
//                     done: true
//                 }
//             }
//         }
//     }
// }

const obj = {
    store: ['foo', 'bar', 'baz'],
    [Symbol.iterator]: function() {
        let index = 0;
        const self = this;
        return {
            next: function() {
                const result = {
                    value: self.store[index],
                    done: index >= self.store.length
                }
                index++;
                return result
            }
        }
    }
}
for (const item of obj) {
    console.log('循环体', item)
}

//迭代器设计模式

//场景：你我协同开发一个任务清单应用

//我的代码

const todos = {
    life: ['吃饭', '睡觉', '打豆豆'],
    learn: ['语文', '数学', '外语'],
    work: ['喝茶'],
    each: function(callback) {
        const all = [].concat(this.life, this.learn, this.work)
        for (const item of all) {
            callback(item)
        }
    },
    [Symbol.iterator]: function() {
        const all = [...this.life, ...this.learn, ...this.work] //将数组展开组合成一个新的数组
        let index = 0;
        return {
            next: function() {
                return {
                    value: all[index],
                    done: index++ >= all.length
                }
            }
        }
    }
}
todos.each(function(item) {
    console.log(item)
})
console.log('------------')

for (const item of todos) {
    console.log(item)
}

//生成器
// function* foo() {
//     console.log('zce')
//     return 100;
// }
// const result = foo()
// console.log(result.next())
function* foo() {
    console.log('111')
    yield 100
    console.log('2222')
    yield 200
    console.log('333')
    yield 300
}
const generator = foo()
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())

//生成器应用

//发号器

// function* createIdMaker() {
//     let id = 1
//     while (true) {
//         yield id++
//     }
// }
// const idMaker = createIdMaker();
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
const ite = {
    [Symbol.iterator]: function* createIdMaker() {
        let id = 1
        while (id < 10) {
            yield id++
        }
    }
}

for (item of ite) {
    console.log(item)
}

//使用Generator函数实现iterator方法
const todos = {
    life: ['吃饭', '睡觉', '打豆豆'],
    learn: ['语文', '数学', '外语'],
    work: ['喝茶'],
    [Symbol.iterator]: function*() {
        const all = [...this.life, ...this.learn, ...this.work] //将数组展开组合成一个新的数组
        for (const item of all) {
            yield item
        }
    }
}
for (const item of todos) {
    console.log(item)
}

//es2016

//includes
const arr = ['foo', 1, NaN]
console.log(arr.includes(NaN)) //true

//指数运算符
// console.log(Math.pow(2, 10))
console.log(2 ** 10)

//es2017
const obj = {
    foo: 'value1',
    bar: 'value2'
};
//Object.values
console.log(Object.values(obj));
//Object.entries
console.log(Object.entries(obj));
for (const [key, value] of Object.entries(obj)) {
    console.log(key, value)
}
console.log(new Map(Object.entries(obj)));
//Object.getOwnPropertyDescriptors
const p1 = {
    firstName: 'lei',
    lastName: 'la',
    get fullName() {
        return this.firstName + ' ' + this.lastName
    }
};
// const p2 = Object.assign({}, p1)//这里把fullName当普通属性复制，所以fullName不会随着firstName的改变而改变
// p2.firstName = 'zce'
// console.log(p2)

const descriptors = Object.getOwnPropertyDescriptors(p1);
// console.log(descriptors);
// const p2 = Object.assign({}, descriptors)
const p2 = Object.defineProperties({}, descriptors)
p2.firstName = 'zce'
console.log(p2.fullName)

//String.prototype.padStart/String.prototype.padEnd
const books = {
    html: 5,
    js: 4,
    css: 6
}
for (const [name, count] of Object.entries(books)) {
    console.log(`${name.padEnd(16,'-')}
    |${count.toString().padStart(3,'0')}`) //使用padEnd，用'-'补充对齐字符串
        //使用padStart给字符串填0,保留3位字符
}